package io.bdmc.core.snmp;

import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import io.bdmc.common.utils.Constant;
import io.bdmc.common.utils.DateUtils;
import io.bdmc.common.utils.EmailUtils;
import io.bdmc.common.utils.SMSUtils;
import io.bdmc.common.websocket.MyWebSocket;
import io.bdmc.core.snmp.cache.ConcurrentHashMapCacheUtils;
import io.bdmc.modules.bss.entity.*;
import io.bdmc.modules.bss.model.AlarmDataType;
import io.bdmc.modules.bss.service.*;
import io.bdmc.modules.sys.entity.SysAbout;
import io.bdmc.modules.sys.service.ISysAboutService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

@Component
@Slf4j
public class SnmpAlarmProcess {

    @Autowired
    IDeviceAlarmTypeService _alarmTypeSvs;

    @Autowired
    IDeviceFibreMatchService _dfmSvs;

    @Autowired
    IFibreFibreService _fibreSvs;

    @Autowired
    IDeviceAlarmService _alarmSvs;

    @Autowired
    IDeviceAlarmRealtimeService _alarmRealtimeSvs;

    @Autowired
    ISysAboutService _aboutSvs;

    @Autowired
    IDeviceDeviceService _deviceService;

    @Autowired
    IDeviceAlarmLevelService _alarmLevelService;

    @Autowired
    IDeviceAlarmMaskService _alarmMaskService;

    @Autowired
    IFibreCablePointService _cablePointService;

    @Autowired
    SMSUtils smsUtils;

    /**
     * 推送报警消息到web客户端
     * 
     * @param alarmType 报警类型
     * @param alarm     报警信息
     * @param levelCode 等级
     */
    @Async
    public void sendAlarmToWebsocketClient(DeviceAlarmType alarmType, DeviceAlarmRealtime alarm, String levelCode) {
        System.out.println("task started!---报警推送推送");
        DeviceAlarmLevel level = _alarmLevelService.getByAlarmLevelCode(levelCode);
        JSONObject json = new JSONObject();
        if (alarmType != null) {
            json.put("playVoice", level.getPlayVoice());
            json.put("displayColor", level.getDisplayColor());
        }

        json.put("alarmTypeName", alarm.getAlarmTypeName());
        json.put("startTime", DateUtils.formatLocalDateTimeDefault(alarm.getStartTime()));
        json.put("alarmLevel", alarm.getAlarmLevel());
        json.put("alarmTypeCode", alarm.getAlarmTypeCode());
        json.put("msgType", "1");
        try {
            MyWebSocket.sendToAllClient(json.toJSONString());
        } catch (Exception e) {
            log.error(e.toString());
        }
        System.out.println("task finished!");

    }

    /**
     * 发送报警的邮件
     * 
     * @param deviceSn 设备sn
     * @param channel  通道号
     * @param alarmMsg 告警消息
     * @param now      时间
     */
    @Async
    public void sendLengthAlarmEmail(String deviceSn, Integer channel, String alarmMsg, LocalDateTime now) {
        try {
            System.out.println("task started!---报警邮箱推送");
            SysAbout about = _aboutSvs.getOne(new QueryWrapper<SysAbout>().last("LIMIT 1"));

            QueryWrapper<DeviceFibreMatch> w1 = new QueryWrapper<>();
            w1.eq("device_sn", deviceSn).eq("channel", channel).last("LIMIT 1");
            DeviceFibreMatch dfm = _dfmSvs.getOne(w1);
            if (dfm == null)
                return;
            DeviceDevice dd = _deviceService.getDeviceBySn(dfm.getDeviceSn());
            FibreFibre ff = _fibreSvs.getById(dfm.getFibreId());
            if (ff == null)
                return;

            if (about.getMailEnable()) {
                String mailAlarmMsg = "";
                mailAlarmMsg += dd.getDeviceName() + "------" + dd.getDeviceSn() + "------";
                mailAlarmMsg += ff.getFibreName() + "------" + dfm.getFibreCoreIndex() + "芯------"
                        + alarmMsg.replace("|", "------") + "------" + DateUtils.formatLocalDateTimeDefault(now);

                List<String> alarmMsgList = new ArrayList<>();
                alarmMsgList.add(mailAlarmMsg);
                log.info(mailAlarmMsg);
                EmailUtils.sendHtmlMail(about.getMailHost(), about.getMailUsername(), about.getMailPassword(), "光纤长度告警",
                        alarmMsgList, about.getMailReceiver(), true);
            }
            if (about.getSmsEnable()) {
                String smsAlarmMsg = "光纤长度告警，光缆：" + ff.getFibreName() + "，设备：" + dd.getDeviceName() + dd.getDeviceSn()
                        + dfm.getFibreCoreIndex() + "芯，" + alarmMsg + "，时间："
                        + DateUtils.formatLocalDateTimeDefault(now);
                for (String m : about.getSmsReceiver().split(",")) {
                    SnmpSMSData smsData = new SnmpSMSData(about.getSmsIp(), about.getSmsPort(), m, smsAlarmMsg);
                    SnmpDataCache.offSmsQueue(smsData);
                    SnmpDataCache.offSmsQueue(smsData);
                }
            }
        } catch (Exception e) {
            log.error(e.toString());
        }

    }

    /**
     * 光纤中断恢复的邮件
     * 
     * @param deviceSn 设备sn
     * @param channel  通道号
     */
    @Async
    public void sendFibreNormalEmail(String deviceSn, Integer channel) {
        log.info("sendFibreNormalEmail-" + deviceSn + "," + channel);
        boolean isAlarm = ConcurrentHashMapCacheUtils.getAlarmEmailCache(deviceSn, channel);
        if (!isAlarm) {
            return;
        }

        try {
            log.info("task started!---光纤中断恢复邮箱推送");
            SysAbout about = _aboutSvs.getOne(new QueryWrapper<SysAbout>().last("LIMIT 1"));
            QueryWrapper<DeviceFibreMatch> w1 = new QueryWrapper<>();
            w1.eq("device_sn", deviceSn).eq("channel", channel).last("LIMIT 1");
            DeviceFibreMatch dfm = _dfmSvs.getOne(w1);
            if (dfm == null)
                return;
            DeviceDevice dd = _deviceService.getDeviceBySn(dfm.getDeviceSn());
            FibreFibre ff = _fibreSvs.getById(dfm.getFibreId());
            if (ff == null)
                return;
            LocalDateTime now = LocalDateTime.now();
            if (about.getMailEnable()) {
                String mailAlarmMsg = "";
                mailAlarmMsg += dd.getDeviceName() + "------" + dd.getDeviceSn() + "------";
                mailAlarmMsg += ff.getFibreName() + "------" + dfm.getFibreCoreIndex() + "芯------"
                        + DateUtils.formatLocalDateTimeDefault(now);

                List<String> alarmMsgList = new ArrayList<>();
                alarmMsgList.add(mailAlarmMsg);
                EmailUtils.sendHtmlMail(about.getMailHost(), about.getMailUsername(), about.getMailPassword(), "光纤中断恢复",
                        alarmMsgList, about.getMailReceiver(), false);
            }
            if (about.getSmsEnable()) {
                String smsContent = "光纤中断恢复，光缆：" + ff.getFibreName() + "，设备：" + dd.getDeviceName() + dd.getDeviceSn()
                        + dfm.getFibreCoreIndex() + "芯，时间：" + DateUtils.formatLocalDateTimeDefault(now);
                for (String m : about.getSmsReceiver().split(",")) {
                    SnmpSMSData smsData = new SnmpSMSData(about.getSmsIp(), about.getSmsPort(), m, smsContent);
                    SnmpDataCache.offSmsQueue(smsData);
                    SnmpDataCache.offSmsQueue(smsData);
                }
            }
        } catch (Exception e) {
            log.error(e.toString());
        }
        System.out.println("task finished!");
        ConcurrentHashMapCacheUtils.setAlarmEmailCache(deviceSn, channel, false);
    }

    /**
     * 推送消息到web客户端,只刷新下面的报警内容
     * 
     * @param msgType msgType
     */
    @Async
    public void sendRefreshToWebsocketClient(int msgType) {
        System.out.println("task started!---刷新信息推送");
        JSONObject json = new JSONObject();
        json.put("msgType", msgType);
        try {
            MyWebSocket.sendToAllClient(json.toJSONString());
        } catch (Exception e) {
            log.error(e.toString());
        }
        System.out.println("task finished!");
    }

    /**
     * 接受并判断和保存trap的报警数据
     */
    public void receiveTrapAlarm(String trapAlarmStr, Integer deptId, Integer deviceId, String deviceSn) {
        String alCode = "", alarmMsg = "";
        boolean needEmail = false;
        int channel = 0;
        final String[] args = trapAlarmStr.split(",");
        final LocalDateTime now = LocalDateTime.now();
        if (args[2].equals("E")) {// 正常 则需要消除指定通道的告警
            channel = Integer.parseInt(args[1].substring(args[1].length() - 1));
            channelOKProcess(deviceSn, channel);
            sendFibreNormalEmail(deviceSn, channel);
            return;
        }
        if (args[1].startsWith(Constant.ALARM_Power)) {
            alCode = Constant.ALARM_Power;
            channel = 0;
            if (Integer.parseInt(args[3]) == 19) {
                alarmMsg = "220V电压异常";
            } else if (Integer.parseInt(args[3]) == 20) {
                alarmMsg = "48V电压异常";
            }
        } else if (args[1].startsWith(Constant.ALARM_Module)) {
            alCode = Constant.ALARM_Module;
            channel = Integer.parseInt(args[1].substring(2));
            alarmMsg = "模块通信异常";
        } else if (args[1].startsWith(Constant.ALARM_Distance)) {
            channel = Integer.parseInt(args[1].substring(2));
            if (Integer.parseInt(args[3]) == 6 || Integer.parseInt(args[3]) == 5) {
                alCode = Constant.ALARM_Loss;
                alarmMsg = String.format("光缆衰耗变化前%sdB,变化后%sdB", args[5], args[6]);
            } else if (Integer.parseInt(args[3]) == 3) {
                alCode = Constant.ALARM_Distance;
                String location = calcLocation(deviceSn, channel, args[6]);
                BigDecimal bd = new BigDecimal(args[6]);
                bd = bd.setScale(4, BigDecimal.ROUND_HALF_UP);
                alarmMsg = String.format("光缆长度变化前%skm,变化后%skm,经纬度位置:%s", args[5], bd, location);
                // 需要发送邮箱
                needEmail = true;
            }

        } else if (args[1].equals(Constant.ALARM_Fan)) {
            alCode = Constant.ALARM_Fan;
            channel = 0;
            alarmMsg = "设备风扇异常";
        } else if (args[1].equals(Constant.ALARM_Temp)) {
            alCode = Constant.ALARM_Temp;
            channel = 0;
            if (Integer.parseInt(args[7]) == 0) {// 超过温度下限值
                alarmMsg = String.format("设备温度低于下限，当前%s度,下限阈值%s度", args[6], args[5]);
            } else if (Integer.parseInt(args[7]) == 1) {// 超过温度下限值
                alarmMsg = String.format("设备温度高于上限，当前温度%s度,上限阈值%s度", args[6], args[5]);
            }
        }

        // 查看报警是否被屏蔽
        if (_alarmMaskService.isAlarmMask(deviceSn, alCode)) {
            return;
        }
        // 判断是否存在该通道告警
        DeviceAlarmRealtime existAlarm = deviceAlarmExist(deviceSn, alCode, channel);
        if (existAlarm != null) {
            existAlarm.setAlarmLevel(args[2]);
            existAlarm.setAlarmSource(Integer.parseInt(args[3]));
            existAlarm.setMonitorValue1(args[5]);
            existAlarm.setMonitorValue2(args[6]);
            existAlarm.setMonitorValue3(args[7]);
            existAlarm.setLastTime(now);
            existAlarm.setAlarmMsg(alarmMsg);
            existAlarm.updateById();
            sendRefreshToWebsocketClient(0);
            return;
        }
        final DeviceAlarmRealtime alarm = new DeviceAlarmRealtime();
        final DeviceAlarmType code = SnmpDataCache.getAlarmTypeListByCode(alCode, args[2]);
        if (code != null) {
            alarm.setAlarmTypeId(code.getAlarmTypeId());
        }
        alarm.setDataType(AlarmDataType.IFMS1000.name());
        alarm.setDeptId(deptId);
        alarm.setDeviceId(deviceId);
        alarm.setDeviceSn(deviceSn);
        alarm.setStartTime(now);
        alarm.setAlarmTypeCode(alCode);
        alarm.setChannel(channel);
        alarm.setLastTime(now);
        alarm.setAlarmLevel(args[2]);
        alarm.setAlarmSource(Integer.parseInt(args[3]));
        alarm.setMonitorValue1(args[5]);
        alarm.setMonitorValue2(args[6]);
        alarm.setMonitorValue3(args[7]);
        alarm.setAlarmMsg(alarmMsg);
        alarm.setProcessed(false);
        alarm.setCreateTime(now);
        DeviceAlarmType alarmType = SnmpDataCache.getAlarmTypeListByCode(alarm.getAlarmTypeCode(), args[2]);
        if (alarmType != null) {
            alarm.setAlarmTypeName(alarmType.getAlarmTypeName());
            alarm.setAlarmTypeId(alarmType.getAlarmTypeId());
        }

        alarm.insert();
        sendAlarmToWebsocketClient(alarmType, alarm, args[2]);
        if (needEmail) {
            // 设置缓存
            ConcurrentHashMapCacheUtils.setAlarmEmailCache(deviceSn, channel, true);
            sendLengthAlarmEmail(deviceSn, channel, alarmMsg, now);
        }

    }

    /**
     * 指定通道发送正常信息，就结束该通道报警
     */
    void channelOKProcess(String deviceSn, int channel) {
        QueryWrapper<DeviceAlarmRealtime> w1 = new QueryWrapper<>();
        w1.eq("channel", channel).eq("device_sn", deviceSn)
                .and(i -> i.eq("alarm_type_code", Constant.ALARM_Distance).or().eq("alarm_type_code",
                        Constant.ALARM_Loss));
        List<DeviceAlarmRealtime> list = _alarmRealtimeSvs.list(w1);
        for (DeviceAlarmRealtime alarmRealtime : list) {
            DeviceAlarm alarm = new DeviceAlarm();
            alarm.setAlarmLevel(alarmRealtime.getAlarmLevel());
            alarm.setAlarmMsg(alarmRealtime.getAlarmMsg());
            alarm.setAlarmSource(alarmRealtime.getAlarmSource());
            alarm.setAlarmTypeCode(alarmRealtime.getAlarmTypeCode());
            alarm.setAlarmTypeId(alarmRealtime.getAlarmTypeId());
            alarm.setAlarmTypeName(alarmRealtime.getAlarmTypeName());
            alarm.setChannel(alarmRealtime.getChannel());
            alarm.setCreateTime(alarmRealtime.getCreateTime());
            alarm.setDeviceId(alarmRealtime.getDeviceId());
            alarm.setDeviceSn(alarmRealtime.getDeviceSn());
            alarm.setStartTime(alarmRealtime.getStartTime());
            alarm.setMonitorValue1(alarmRealtime.getMonitorValue1());
            alarm.setMonitorValue2(alarmRealtime.getMonitorValue2());
            alarm.setMonitorValue3(alarmRealtime.getMonitorValue3());
            alarm.setDeptId(alarmRealtime.getDeptId());
            alarm.setRemark("设备发送通道已正常信息");
            alarm.setProcessed(true);
            alarm.setProcessBy("system");
            alarm.setProcessTime(LocalDateTime.now());
            alarm.setEndTime(alarmRealtime.getLastTime());
            alarm.insert();
        }
        _alarmRealtimeSvs.remove(w1);
        sendRefreshToWebsocketClient(3);
    }

    // /**
    // * 查询指定设备的报警代码是否被屏蔽
    // *
    // *
    // * @param alarmCode 报警代码
    // * @return
    // */
    // boolean isAlarmTypeBlocked(Integer deviceId, String alarmCode) {
    //
    // QueryWrapper<DeviceAlarmType> w1 = new
    // QueryWrapper<DeviceAlarmType>().eq("enabled", true).eq("alarm_type_code",
    // alarmCode);
    // int count = _alarmTypeSvs.count(w1);
    // return count > 0;
    // }

    /**
     * 同一通道是否有已存在的报警类型，有则更新数据
     * 
     * @param deviceSn  设备sn
     * @param alarmCode 报警代码
     * @param channel   通道
     * @return 当前告警
     */
    DeviceAlarmRealtime deviceAlarmExist(String deviceSn, String alarmCode, int channel) {
        QueryWrapper<DeviceAlarmRealtime> w1 = new QueryWrapper<DeviceAlarmRealtime>().eq("device_sn", deviceSn)
                .eq("alarm_type_code", alarmCode).eq("channel", channel).last("LIMIT 1");
        return _alarmRealtimeSvs.getOne(w1);
    }

    public String calcLocation(String deviceSn, int channel, BigDecimal alarmLength) {
        DeviceFibreMatch dfm = _dfmSvs.selectByDeviceSnAndChannel(deviceSn, channel);
        return calcLocation(dfm, alarmLength);
    }

    public String calcLocation(DeviceFibreMatch dfm, BigDecimal alarmLength) {
        String location = "-1,-1";
        try {
            if (dfm == null || dfm.getFibreId() == null) {
                log.error("设备未绑定光缆");
                return location;
            }

            // 查询光纤绑定的设施点
            List<FibreCablePoint> cablePointsList = _cablePointService.selectByFibreId(dfm.getFibreId());
            // 有设施点就根据长度计算报警位置
            if (cablePointsList.size() > 0) {
                // 计算报警位置
                cablePointsList.sort(Comparator.comparing(FibreCablePoint::getOrigoLength));
                BigDecimal alarmX;
                BigDecimal alarmY;

                // 起点就不用计算，取第一个点的经纬度
                if (alarmLength.compareTo(new BigDecimal("0.1")) < 0) {
                    alarmX = new BigDecimal(cablePointsList.get(0).getLng()).setScale(6,
                            BigDecimal.ROUND_DOWN);
                    alarmY = new BigDecimal(cablePointsList.get(0).getLat()).setScale(6,
                            BigDecimal.ROUND_DOWN);
                    location = alarmX.toString() + "," + alarmY.toString();
                    return location;
                }
                for (int i = 0; i < cablePointsList.size(); i++) {
                    if (cablePointsList.get(i).getOrigoLength().compareTo(alarmLength) > 0) {
                        if (i == 0) {
                            // 第0个点时，从根部断
                            return cablePointsList.get(0).getLng() + "," + cablePointsList.get(0).getLat();
                        }
                        BigDecimal aOrigoLength = cablePointsList.get(i - 1).getOrigoLength();
                        BigDecimal bOrigoLength = cablePointsList.get(i).getOrigoLength();
                        alarmX = new BigDecimal(cablePointsList.get(i).getLng())
                                .subtract(new BigDecimal(cablePointsList.get(i - 1).getLng()));
                        alarmY = new BigDecimal(cablePointsList.get(i).getLat())
                                .subtract(new BigDecimal(cablePointsList.get(i - 1).getLat()));
                        BigDecimal aLocation = aOrigoLength.subtract(alarmLength).abs();
                        BigDecimal abLocation = bOrigoLength.subtract(aOrigoLength);
                        BigDecimal aLocationAlarmX = alarmX.multiply(aLocation)
                                .divide(abLocation, 6, BigDecimal.ROUND_HALF_UP)
                                .add(new BigDecimal(cablePointsList.get(i - 1).getLng()));
                        BigDecimal aLocationAlarmY = alarmY.multiply(aLocation)
                                .divide(abLocation, 6, BigDecimal.ROUND_HALF_UP)
                                .add(new BigDecimal(cablePointsList.get(i - 1).getLat()));
                        location = aLocationAlarmX.setScale(6, BigDecimal.ROUND_DOWN).toString() + ","
                                + aLocationAlarmY.setScale(6, BigDecimal.ROUND_DOWN).toString();
                        break;
                    }
                }
            }
        } catch (Exception ex) {
            log.error("经纬度计算异常:" + ex.getMessage());
        }
        log.error("经纬度计算结果:" + location);
        return location;
    }

    public String calcLocation(String deviceSn, int channel, String alarmLengthStr) {
        // 报警的光纤长度位置
        BigDecimal alarmLength = new BigDecimal(alarmLengthStr);
        return calcLocation(deviceSn, channel, alarmLength);
    }
}
